#include "socketclient.h"
#include <QThread>
#include <QByteArray>

#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <memory.h>

#include <time.h>
#include "agreement/agreement.h"
#include "agreement/obd_shanghai.h"
#include "DateTime.h"
#include "agreement/upload.h"

enum ssl_type SocketClient::ssl;
static QTcpSocket *socket=NULL;
static const struct agreement_ofp* _agree_obd=NULL;

static uint8_t obd_buf[1024*10];
static uint8_t msg_buf[4096];
static struct report_gps gps;
static OBD_DATA obd;

SocketClient::SocketClient(const QString _hostName, const quint16 _port, QObject *parent) : QObject(parent)
{
    memset(&gps, 0, sizeof (gps));
    gps.status = 0x3<<1;
    gps.speed = 30;
    gps.longitude = 30;
    gps.latitude = 40;
    memset(&obd, 0, sizeof (obd));
    memcpy(obd.ECUInfo.VIN, "VIN1234567890ABCDEFGHIJ", 19);
    obd.RTData.Velocity = 100;
    obd.RTData.EngineRev = 200;
    obd.RTData.Mileage = 300;
    obd.RTData.FuelConsumption = 400;
#if 1
    // 1. 创建TCP套接字对象
    socket = new QTcpSocket(this);
    socket->abort();        //取消原有连接
    // 2. 已连接、数据可读、失败信号连接
    //connect(socket, &QTcpSocket::connected, this, &SocketClient::connected);
    connect(socket, &QIODevice::readyRead, this, &SocketClient::readyRead);
    typedef void (QAbstractSocket::*QAbstractSocketErrorSignal)(QAbstractSocket::SocketError);
    connect(socket, static_cast<QAbstractSocketErrorSignal>(&QTcpSocket::error), this, &SocketClient::error);
    //3. 与服务器端建立连接
    //socket->connectToHost("39.108.72.130", 9910, QTcpSocket::ReadWrite);
    socket->connectToHost(_hostName, _port, QTcpSocket::ReadWrite);
    socket->waitForConnected();
    //4. 同步处理-等待数据可读
    //socket->waitForReadyRead();
#if 0
    socket->write("Hello", 5);
#endif
    _agree_obd = create_agree_obd_shanghai();
    //_agree_obd->init(0, (const uint8_t*)"IMEI1234567890ABCDEF", 2, "INFO");
    //_agree_obd->init(0, (const uint8_t*)"IMEI1234567890ABCDEF", 2, "RSA");
    switch(ssl)
    {
        case SSL_RSA:
            _agree_obd->init(0, (const uint8_t*)"IMEI1234567890ABCDEF", 2, "RSA");
            break;
        case SSL_SM2:
            _agree_obd->init(0, (const uint8_t*)"IMEI1234567890ABCDEF", 2, "INFO");
            break;
        case SSL_INFO:
        default:
            _agree_obd->init(0, (const uint8_t*)"IMEI1234567890ABCDEF", 2, "INFO");
            break;
    }
#else
    char buf[1024];
    // 1. 创建TCP套接字对象
    socket = new QTcpSocket(this);
    // 2. 已连接、数据可读、失败信号连接
//    connect(socket, &QTcpSocket::connected, this, &SocketClient::connected);
//    connect(socket, &QIODevice::readyRead, this, &SocketClient::readyRead);
//    typedef void (QAbstractSocket::*QAbstractSocketErrorSignal)(QAbstractSocket::SocketError);
//    connect(socket, static_cast<QAbstractSocketErrorSignal>(&QTcpSocket::error), this, &SocketClient::error);
    //3. 与服务器端建立连接
    socket->connectToHost("39.108.72.130", 9910);
    socket->waitForConnected();
//    connect(socket, &QTcpSocket::connected, this, &SocketClient::connected);
//    connect(socket, &QIODevice::readyRead, this, &SocketClient::readyRead);
//    typedef void (QAbstractSocket::*QAbstractSocketErrorSignal)(QAbstractSocket::SocketError);
//    connect(socket, static_cast<QAbstractSocketErrorSignal>(&QTcpSocket::error), this, &SocketClient::error);
    //4. 同步处理-等待数据可读
    //socket->waitForReadyRead();
    socket->write("Hello", 5);
    socket->waitForBytesWritten();
    QThread::msleep(100);
    socket->waitForReadyRead(3000);
    socket->read(buf, sizeof (buf));
    qDebug("read: %s\n", buf);
    socket->waitForReadyRead(3000);
    socket->read(buf, sizeof (buf));
    qDebug("read: %s\n", buf);
    //int len = test_msg_verify();
    //qDebug("test_msg_login: %d\n", len);
    //socket->flush();
#endif
}

void SocketClient::login()
{
    int len = 0;
    time_t timer = time(NULL);
    len = _agree_obd->login(timer, 0, (const uint8_t*)"VIN1234567890ABCDEF1234567890",  (const uint8_t*)"VIN1234567890ABCDEF", "ATT", obd_buf, sizeof (obd_buf));
    printf("login pack len : %d\n", len);
    fflush(stdout);
//    decode_test(_agree_obd, obd_buf, (const uint16_t)len, msg_buf, sizeof (msg_buf));
//    fflush(stdout);
    //printf("write:%d\n", socket->write((char*)obd_buf, len)); fflush(stdout);
    socket->write((char*)obd_buf, len);
    socket->waitForBytesWritten();
    uint8_t buffer[sizeof(struct upload)];
    // upload_init(const enum upload_cmd _cmd, const uint16_t pack_total, const uint16_t pack_index, const uint32_t checksum, const uint8_t Model[16], const uint8_t data[512], const uint16_t data_len);
    struct upload* _load = upload_init(UPLOAD_LOGIN, 0, 0, 0x12345678, (const char*)"OBD1234567890ABCDEF", "Hello", 5);
    memset(buffer, 0, sizeof (buffer));
    len = upload_encode(_load, buffer, sizeof (buffer));
    len = _agree_obd->encode(buffer, len, obd_buf, sizeof (obd_buf));
    //printf("login pack encode len : %d\n", len);
    socket->write((char*)obd_buf, len);
    socket->waitForBytesWritten();
}

void SocketClient::logout()
{
    int len = 0;
    time_t timer = time(NULL);
    len = _agree_obd->logout(timer, 0, obd_buf, sizeof (obd_buf));
    //printf("login pack len : %d\n", len);
    fflush(stdout);
//    decode_test(_agree_obd, obd_buf, (const uint16_t)len, msg_buf, sizeof (msg_buf));
//    fflush(stdout);
    //printf("write:%d\n", socket->write((char*)obd_buf, len)); fflush(stdout);
    socket->write((char*)obd_buf, len);
    socket->waitForBytesWritten();
}

void SocketClient::UTC()
{
    int len = 0;
    //time_t timer = time(NULL);
    len = _agree_obd->utc(obd_buf, sizeof (obd_buf));
    //printf("login pack len : %d\n", len);
    fflush(stdout);
//    decode_test(_agree_obd, obd_buf, (const uint16_t)len, msg_buf, sizeof (msg_buf));
//    fflush(stdout);
    //printf("write:%d\n", socket->write((char*)obd_buf, len)); fflush(stdout);
    socket->write((char*)obd_buf, len);
    socket->waitForBytesWritten();
}

void SocketClient::report()
{
    int len = 0;
    time_t timer = time(NULL);
    gps.speed = (gps.speed+1)%125;
    gps.longitude = (gps.longitude+1)%180;
    gps.latitude = (gps.latitude+1)%180;
    obd.RTData.Velocity = (obd.RTData.Velocity+5)%700;
    obd.RTData.EngineRev = (obd.RTData.EngineRev+13)%1700;
    obd.RTData.Mileage = (obd.RTData.Mileage+3)%800;
    obd.RTData.FuelConsumption = (obd.RTData.FuelConsumption+7)%900;
    obd.DTCInfo.Current = 3;
    obd.DTCInfo.CurrentDTC[0] = '0';
    obd.DTCInfo.CurrentDTC[1] = '1';
    obd.DTCInfo.CurrentDTC[2] = '2';
    obd.DTCInfo.CurrentDTC[3] = '3';
    obd.DTCInfo.CurrentDTC[4] = '4';
    obd.DTCInfo.CurrentDTC[5] = '5';
    time(&timer);
    len = _agree_obd->report(shanghai_report_msg(timer, &obd, &gps, msg_buf, sizeof (msg_buf)), obd_buf, sizeof (obd_buf));
    //printf("report pack len : %d\n", len);
    fflush(stdout);
//    decode_test(_agree_obd, obd_buf, (const uint16_t)len, msg_buf, sizeof (msg_buf));
//    fflush(stdout);
    //printf("write:%d\n", socket->write((char*)obd_buf, len)); fflush(stdout);
    socket->write((char*)obd_buf, len);
    socket->waitForBytesWritten();
}

void SocketClient::report_later()
{
    int len = 0;
    time_t timer = time(NULL);
    gps.speed = (gps.speed+1)%125;
    gps.longitude = (gps.longitude+1)%180;
    gps.latitude = (gps.latitude+1)%180;
    obd.RTData.Velocity = (obd.RTData.Velocity+5)%700;
    obd.RTData.EngineRev = (obd.RTData.EngineRev+13)%1700;
    obd.RTData.Mileage = (obd.RTData.Mileage+3)%800;
    obd.RTData.FuelConsumption = (obd.RTData.FuelConsumption+7)%900;
    time(&timer);
    len = _agree_obd->report_later(shanghai_report_msg(timer, &obd, &gps, msg_buf, sizeof (msg_buf)), obd_buf, sizeof (obd_buf));
    //printf("report pack len : %d\n", len);
    fflush(stdout);
//    decode_test(_agree_obd, obd_buf, (const uint16_t)len, msg_buf, sizeof (msg_buf));
//    fflush(stdout);
    //printf("write:%d\n", socket->write((char*)obd_buf, len)); fflush(stdout);
    socket->write((const char*)obd_buf, len);
    socket->flush();
    socket->waitForBytesWritten();
}
//static uint8_t msg_buf[4096];
static void csend(const int sockfd, const void *buf, const uint16_t len)
{
    (void)sockfd;
    socket->write((const char*)buf, (qint64)len);
    socket->flush();
    socket->waitForBytesWritten();
}
#if 0
#define   LOAD_FILE   1
static char* load_file(const char* filename, long* const size)
{
    static char filebin[2*1024*1024]; // 2MB
    long _size=0;
    size_t count=0;
    FILE* fd = fopen(filename, "r");
    if(NULL == fd)
    {
        printf("file %s not exist! \n", filename);  fflush(stdout);
        return NULL;
    }
    else
    {
        fseek(fd, 0, SEEK_END);
        _size = ftell(fd);
        fseek(fd, 0, SEEK_SET);
        printf("%s@%d _size:%ld\n", __func__, __LINE__, _size); fflush(stdout);
        // read
        memset(filebin, 0xFF, sizeof (filebin));
        count = fread(filebin, (size_t)_size, 1, fd);
        fclose(fd);
        printf("%s@%d fread _size:%ld count:%lld\n", __func__, __LINE__, _size, count); fflush(stdout);
        //if(1!=count) return NULL;
        //pr_debug("%s@%d fread _size:%d\n", __func__, __LINE__, _size);
        *size = _size;
        return filebin;
    }
}
#else
#define   LOAD_FILE   0
#endif
//enum upload_status{
//      STATUS_QUERY_CFG   = 0x00,
//      STATUS_UPLOAD_CFG  = 0x01,
//      STATUS_QUERY_FW    = 0x02,
//      STATUS_UPLOAD_FW   = 0x03,
//      STATUS_REBOOT      = 0x04,
//      STATUS_JUMP        = 0x05,
//      STATUS_ERR         = 0x06,
//      STATUS_TIMEOUT     = 0x07,
//      STATUS_RUN         = 0x08,
//};
#include<fcntl.h>
//static uint8_t status=0;
static enum upload_status status=STATUS_QUERY_CFG;
static uint8_t timeout = 0;
//struct cfg_data _cfg_data;
#define TIMEOUT     7
#define WSN_LEN     12
#define DOWNLOAD    fw_path  // "download.bin"
void SocketClient::upload()
{
    int len = 0;
    int ret = 0;
    qint64 _size=0;
    const uint8_t wsn[32]="102903420018";
    static char buf[1024*10];
    const char Model[16] = "OBD1234567890AB";
    //time_t timer = time(NULL);
    uint32_t checksum;
    int fd = 0;
#if LOAD_FILE
    char* fw;
    long fw_size=0;
#endif
    uint8_t buffer[sizeof(struct upload)];
    // upload_init(const enum upload_cmd _cmd, const uint16_t pack_total, const uint16_t pack_index, const uint32_t checksum, const uint8_t Model[16], const uint8_t data[512], const uint16_t data_len);
    struct upload* _load = NULL;
    memset(buffer, 0, sizeof (buffer));
    if(STATUS_QUERY_CFG==status)
    {
#if LOAD_FILE
        fw = load_file("./upload/OBD1234567890AB.cfg", &fw_size);  // 获取文件
        if(NULL==fw)
        {
            printf("read file %s fail!\n", "./upload/OBD1234567890AB.cfg"); fflush(stdout);
            checksum = 0x012345678;
        }
        else
        {
            checksum = fast_crc16(0, (unsigned char *)fw, fw_size);
        }
#else
        checksum = crc_form_file(config_path, buf, sizeof(buf) );
#endif
        //printf("%s@%d checksum:0x%04X\n", __func__, __LINE__, checksum); fflush(stdout);
        _load = upload_init(UPLOAD_QUERY_CFG, 0, 10*1024, checksum, Model, wsn, 12);
        len = upload_encode(_load, buffer, sizeof (buffer));
        len = _agree_obd->encode(buffer, len, obd_buf, sizeof (obd_buf));
        //printf("login pack encode len : %d\n", len);
        socket->write((char*)obd_buf, len);
        socket->flush();
        socket->waitForBytesWritten();
        QThread::msleep(1000);
        socket->waitForReadyRead();
        _size = socket->read(buf, sizeof (buf));
        ret = decode_client(_agree_obd, (const uint8_t*)buf, _size, msg_buf, sizeof (msg_buf), 0, csend);
        if(STATUS_CLIENT_DOWN==ret)
        {
            status = STATUS_UPLOAD_CFG;
            timeout = 0;
        }
        if(STATUS_CLIENT_DONE==ret)
        {
            status = STATUS_QUERY_FW;
            timeout = 0;
        }
        timeout++;
        if(_size>0) timeout=0;
        if(timeout>TIMEOUT) status = STATUS_TIMEOUT;
    }
    else if(STATUS_UPLOAD_CFG==status)
    {
        socket->waitForReadyRead();
        _size = socket->read(buf, sizeof (buf));
        ret = decode_client(_agree_obd, (const uint8_t*)buf, _size, msg_buf, sizeof (msg_buf), 0, csend);
        if(STATUS_CLIENT_DONE==ret)
        {
            printf("CFG rename: %s %s\n", download_cfg_temp, config_path); fflush(stdout);
            status = STATUS_QUERY_FW;
            timeout = 0;
            fd = open(config_path, O_RDONLY, 0);
            if(0 <= fd)
            {
                close(fd);
                //remove("/OBD.cfg");
                unlink(config_path);
            }
            //rename(path, "/OBD.cfg");
            rename(download_cfg_temp, config_path);
            //decode_cfg(&_cfg_data, config_path, msg_buf, sizeof (msg_buf));
        }
        if(ERR_CLIENT_DOWN==ret)
        {
            status = STATUS_QUERY_FW;
            timeout = 0;
        }
        timeout++;
        if(_size>0) timeout=0;
        if(timeout>TIMEOUT) status = STATUS_TIMEOUT;
    }
#if 1
    else if(STATUS_QUERY_FW == status)
    {
#if LOAD_FILE
        fw = load_file("./upload/OBD1234567890AB.bin", &fw_size);  // 获取文件
        if(NULL==fw)
        {
            printf("read file %s fail!\n", "./upload/OBD1234567890AB.bin"); fflush(stdout);
            checksum = 0x012345678;
        }
        else
        {
            checksum = fast_crc16(0, (unsigned char *)fw, fw_size);
        }
#else
        checksum = crc_form_file(fw_path, buf, sizeof(buf) );
#endif
        printf("%s@%d checksum:0x%04X\n", __func__, __LINE__, checksum); fflush(stdout);
        _load = upload_init(UPLOAD_QUERY_FW, 0, 10*1024, checksum, Model, wsn, 12);
        len = upload_encode(_load, buffer, sizeof (buffer));
        len = _agree_obd->encode(buffer, len, obd_buf, sizeof (obd_buf));
        //printf("login pack encode len : %d\n", len);
        socket->write((char*)obd_buf, len);
        socket->flush();
        socket->waitForBytesWritten();
        QThread::msleep(1000);
        socket->waitForReadyRead();
        _size = socket->read(buf, sizeof (buf));
        ret = decode_client(_agree_obd, (const uint8_t*)buf, _size, msg_buf, sizeof (msg_buf), 0, csend);
        if(STATUS_CLIENT_DOWN==ret)
        {
            status = STATUS_UPLOAD_FW;
            timeout = 0;
        }
        if(STATUS_CLIENT_DONE==ret)
        {
            status = STATUS_JUMP;
            timeout = 0;
        }
        timeout++;
        if(_size>0) timeout=0;
        if(timeout>TIMEOUT) status = STATUS_TIMEOUT;
    }
    else if(STATUS_UPLOAD_FW==status)
    {
        socket->waitForReadyRead();
        _size = socket->read(buf, sizeof (buf));
        ret = decode_client(_agree_obd, (const uint8_t*)buf, _size, msg_buf, sizeof (msg_buf), 0, csend);
        //printf("rename[%d]: %s %s\n", ret, download_fw_temp, DOWNLOAD); fflush(stdout);
        if(STATUS_CLIENT_DONE==ret)
        {
            status = STATUS_REBOOT;
            timeout = 0;
            //printf("rename: %s %s\n", download_fw_temp, DOWNLOAD); fflush(stdout);
            fd = open(DOWNLOAD, O_RDONLY, 0);
            if(0 <= fd)
            {
                close(fd);
                //remove(DOWNLOAD);
                unlink(DOWNLOAD);
            }
            //rename(path, DOWNLOAD);
            rename(download_fw_temp, DOWNLOAD);
        }
        if(ERR_CLIENT_DOWN==ret)
        {
            status = STATUS_ERR;
            timeout = 0;
        }
        timeout++;
        if(_size>0) timeout=0;
        if(timeout>TIMEOUT) status = STATUS_TIMEOUT;
    }
#endif
    else if(STATUS_TIMEOUT==status)
    {
        status = STATUS_RUN;
    }
    else if(STATUS_REBOOT==status)
    {
        status = STATUS_RUN;
    }
    else if(STATUS_JUMP==status)
    {
        status = STATUS_RUN;
    }
    else if(STATUS_ERR==status)
    {
        status = STATUS_RUN;
    }
    else
    {
        QThread::msleep(1000);
    }
}



void SocketClient::connected()
{
    qDebug() << "SocketClient::readyRead " << socket->readAll();
}

void SocketClient::readyRead()
{
    qDebug() << "SocketClient::connected  successfully";
}

void SocketClient::error(QAbstractSocket::SocketError socketError)
{
    qDebug() << "SocketClient::error " << socketError;
}
